<!--
 * @Description: 流程图预览
 * @Author: DHL
 * @Date: 2022-09-14 11:09:34
 * @LastEditors: DHL
 * @LastEditTime: 2022-11-28 16:48:51
-->
<template>
  <div class="preview-flowchart-wrapper">
    <div class="legend">
      <div class="square complete"></div>
      <div class="txt">已完成</div>

      <div class="square running"></div>
      <div class="txt">进行中</div>

      <div class="square unexecuted"></div>
      <div class="txt">未执行</div>
    </div>

    <div ref="canvasRef" class="canvas"></div>
  </div>
</template>

<script setup lang="ts">
  import BpmnViewer from 'bpmn-js/lib/NavigatedViewer'
  import { ElMessage } from 'element-plus'
  import { apiFlowViewer } from '/@/api/flow/flowViewer'
  import { stringUtils } from '/@/utils/stringUtils'

  //////////////////////////////////////////////////
  // 属性
  //////////////////////////////////////////////////
  const canvasRef = ref()

  const viewerModeler = ref()

  const defaultZoom = ref(1)

  const state = reactive({
    xmlStr: '',
    nodeList: [] as Array<any>
  })

  //////////////////////////////////////////////////
  // 函数
  //////////////////////////////////////////////////

  /**
   * 构建流程图
   * @param procInsId 流程实例
   */
  function build(procInsId: string) {
    nextTick(() => {
      handleSearch(procInsId)
    })
  }

  /**
   * 根据流程实例主键查询流程图
   */
  function handleSearch(procInsId: string) {
    if (stringUtils.isEmpty(procInsId)) {
      ElMessage.warning('流程图预览参数为空')
      return
    }

    apiFlowViewer(procInsId).then((result) => {
      if (result?.success) {
        const { xmlStr, nodeList } = result.data

        state.xmlStr = xmlStr
        state.nodeList = nodeList

        handleBuildBpmnViewer()
      } else {
        ElMessage.error(result?.message)
      }
    })
  }

  /**
   * 构建BpmnViewer
   */
  async function handleBuildBpmnViewer() {
    if (viewerModeler.value) {
      viewerModeler.value.destroy()
    }

    viewerModeler.value = new BpmnViewer({
      container: canvasRef.value
    })

    let { warnings } = await viewerModeler.value.importXML(state.xmlStr)

    handleProcessReZoom()

    handleFillColor()
  }

  /**
   * 重置视图并居中
   */
  function handleProcessReZoom() {
    defaultZoom.value = 1
    viewerModeler.value.get('canvas').zoom('fit-viewport', 'auto')
  }

  /**
   * 设置高亮颜色的
   */
  function handleFillColor() {
    const canvas = viewerModeler.value.get('canvas')

    viewerModeler.value.getDefinitions().rootElements[0].flowElements.forEach((n) => {
      const completeTask = state.nodeList.find((m) => m.key === n.id)
      const todoTask = state.nodeList.find((m) => !m.completed)
      const endTask = state.nodeList[state.nodeList.length - 1]

      if (n.$type === 'bpmn:UserTask') {
        if (completeTask) {
          canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
          n.outgoing?.forEach((nn) => {
            const targetTask = state.nodeList.find((m) => m.key === nn.targetRef.id)
            if (targetTask) {
              if (todoTask && completeTask.key === todoTask.key && !todoTask.completed) {
                canvas.addMarker(nn.id, todoTask.completed ? 'highlight' : 'highlight-todo')
                canvas.addMarker(
                  nn.targetRef.id,
                  todoTask.completed ? 'highlight' : 'highlight-todo'
                )
              } else {
                canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
                canvas.addMarker(
                  nn.targetRef.id,
                  targetTask.completed ? 'highlight' : 'highlight-todo'
                )
              }
            }
          })
        }
      }
      // 排他网关
      else if (n.$type === 'bpmn:ExclusiveGateway') {
        if (completeTask) {
          canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
          n.outgoing?.forEach((nn) => {
            const targetTask = state.nodeList.find((m) => m.key === nn.targetRef.id)
            if (targetTask) {
              canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
              canvas.addMarker(
                nn.targetRef.id,
                targetTask.completed ? 'highlight' : 'highlight-todo'
              )
            }
          })
        }
      }
      // 并行网关
      else if (n.$type === 'bpmn:ParallelGateway') {
        if (completeTask) {
          canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
          n.outgoing?.forEach((nn) => {
            const targetTask = state.nodeList.find((m) => m.key === nn.targetRef.id)
            if (targetTask) {
              canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
              canvas.addMarker(
                nn.targetRef.id,
                targetTask.completed ? 'highlight' : 'highlight-todo'
              )
            }
          })
        }
      } else if (n.$type === 'bpmn:StartEvent') {
        n.outgoing.forEach((nn) => {
          const completeTask = state.nodeList.find((m) => m.key === nn.targetRef.id)
          if (completeTask) {
            canvas.addMarker(nn.id, 'highlight')
            canvas.addMarker(n.id, 'highlight')
            return
          }
        })
      } else if (n.$type === 'bpmn:EndEvent') {
        if (endTask.key === n.id && endTask.completed) {
          canvas.addMarker(n.id, 'highlight')
          return
        }
      }
    })
  }

  //////////////////////////////////////////////////
  // 生命周期
  //////////////////////////////////////////////////

  onMounted(() => {})

  onBeforeUnmount(() => {
    if (viewerModeler.value) {
      viewerModeler.value.destroy
    }
  })

  //////////////////////////////////////////////////
  // 暴露出去，父组件才可以调用到
  //////////////////////////////////////////////////
  defineExpose({
    build
  })
</script>

<style scoped lang="scss">
  // 已办
  $--done: #19be6b;
  // 待办
  $--todo: #ff9900;

  // 退回
  $--back: #fa3534;

  // 未执行
  $--unexecuted: rgb(34 36 42);

  .preview-flowchart-wrapper {
    width: 100%;
    height: 100%;
    min-width: 850px;
    min-height: 650px;

    :deep(.bjs-container) {
      .bjs-powered-by,
      .bjs-breadcrumbs {
        display: none;
      }

      // 节点
      .djs-shape {
        .djs-visual {
          > :nth-child(1) {
            fill-opacity: 0.2 !important;
          }

          .djs-label {
            fill-opacity: 1 !important;
          }
        }

        &.highlight {
          .djs-visual {
            > :nth-child(1) {
              // fill: $--done !important;
              stroke: $--done !important;
              fill-opacity: 0.2 !important;
            }

            .djs-label {
              fill: $--done !important;
            }

            path {
              // fill: $--done !important;
              fill-opacity: 0.2 !important;
              stroke: $--done !important;
            }
          }
        }

        &.highlight-todo {
          .djs-visual {
            > :nth-child(1) {
              // fill: $--todo !important;
              stroke: $--todo !important;
              stroke-dasharray: 4px !important;
              fill-opacity: 0.2 !important;
            }
          }
        }

        &.selected {
          .djs-outline {
            stroke: #4d90ff;
            stroke-width: 1px;
            stroke-dasharray: 3, 3;
          }
        }

        &.hover {
          .djs-outline {
            stroke: #4d90ff;
            stroke-width: 1px;
            stroke-dasharray: 3, 3;
          }
        }
      }

      // 连线
      .djs-connection {
        &.highlight {
          .djs-visual {
            path {
              stroke: $--done !important;
            }
          }
        }

        &.highlight-todo {
          .djs-visual {
            path {
              stroke: $--todo !important;
              stroke-dasharray: 4px !important;
              fill-opacity: 0.2 !important;
            }
          }
        }
      }

      .overlays-div {
        font-size: 10px;
        color: red;
        width: 100px;
        top: -20px !important;
      }
    }

    .legend {
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 10px;

      .item {
        line-height: 22px;
      }

      .square {
        display: inline-block;
        width: 15px;
        height: 15px;
        border-radius: 3px;
      }

      .txt {
        padding: 0px 15px 0px 3px;
      }

      .complete {
        background-color: $--done;
      }

      .running {
        background-color: $--todo;
      }

      .unexecuted {
        background-color: $--unexecuted;
      }
    }
    .canvas {
      cursor: pointer;
      width: 100%;
      height: calc(100% - 45px);
    }
  }
</style>
